package controllers;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

/**
 * Server. Manages Table and PlayerWorkers.
 * Created by grzegorz on 10/14/14.
 */
public class Server {
	private ServerSocket serverSocket;
	private Socket client;
	private Table table;
	private int[] playerIDs = new int[ 6 ];
	private int playerCount;
	ArrayList<PlayerWorker> playerWorkers = new ArrayList<PlayerWorker>();

	public Server ( int port, int smallBlind, int bigBlind, int chips )
	{
		table = new Table( this, smallBlind, bigBlind, chips );
		for( int i = 0; i < playerIDs.length; i ++ )
			playerIDs[ i ] = -1;
		try {
			serverSocket = new ServerSocket( port );
		}
		catch( IOException e ) {
			System.out.println( "Listening on port failed." );
			System.exit( 1 );
		}
	}

	public void listenSocket() {
		playerCount = 0;
		while( true ) {
			if( playerCount == 6 )
				continue;
			PlayerWorker clientWorker;
			try {
				client = serverSocket.accept();
				int playerID = 0;
				for( int i = 0; i < playerIDs.length; i++ ) {
					if( playerID == playerIDs[ i ] )
						playerID++;
				}
				playerIDs[ playerID ] = playerID;
				clientWorker = new PlayerWorker( this, client, table, playerID );
				playerWorkers.add( clientWorker );
				table.addPlayer( playerID );
				Thread thread = new Thread( clientWorker );
				thread.start();
				playerCount++;
			}
			catch( IOException e ) {
				System.out.println( "Accept failed." );
				System.exit( 1 );
			}
		}
	}

	/**
	 * Called when a player disconnects.
	 * @param playerWorker the PlayerWorker responsible for the disconnected player.
	 */
	public void playerDisconnected( PlayerWorker playerWorker ) {
		for( int i = 0; i < playerIDs.length; i++ ) {
			if( playerWorker.getID() == playerIDs[ i ] )
				playerIDs[ i ] = -1;
		}
		playerCount--;
		playerWorkers.remove( playerWorker );
		System.out.println( "Disconnected." );
	}

	private PlayerWorker getPlayerWorker( int id ) {
		for( PlayerWorker worker : playerWorkers ) {
			if( worker.getID() == id )
				return worker;
		}
		return null;
	}

	public void sendTurnInfo( int id, char message ) {
		PlayerWorker playerWorker = getPlayerWorker( id );
		Socket client;
		if( playerWorker == null )
			return;
		client = playerWorker.getClient();
		try {
			PrintWriter out = new PrintWriter( client.getOutputStream(), true );
			System.out.println( "YourTurn, " + id + " " + message );
			out.println( "YourTurn" );
			switch( message ) {
				case 'C':
					out.println( "You change." );
					break;
				case 'B':
					out.println( "You bid." );
					break;
			}
		}
		catch( IOException e ) {
			System.out.println( "I/O error. Turn." );
		}
	}

	/**
	 * Sends info about cards at the end of the game.
	 */
	public void sendCardInfo( boolean showAll, int id ) {
		for( PlayerWorker playerWorker : playerWorkers ) {
			Socket client = playerWorker.getClient();
			try {
				PrintWriter out = new PrintWriter( client.getOutputStream(), true );
				int workerID = playerWorker.getID();
				if( showAll ) {
					for( Player player : table.getPlayers() ) {
						out.println( "Cards" );
						out.println( player.getID() );
						Card[] cards = player.getCards();
						for( Card card : cards ) {
							out.println( card.getRank() );
							out.println( card.getSuit() );
						}
					}
				}
				else if( workerID == id ) {
					Player player = table.getPlayers().get( id );
					Card[] cards = player.getCards();
					out.println( "Cards" );
					System.out.println( "Cards" );
					out.println( player.getID() );
					System.out.println( player.getID() );
					for( Card card : cards ) {
						out.println( card.getRank() );
						out.println( card.getSuit() );
						System.out.println( card.getRank() );
						System.out.println( card.getSuit() );
					}
				}

			}
			catch( IOException e ) {
				System.out.println( "I/O error. EndCards." );
			}
		}
	}

	public void sendMessage( String message, int id ) {
		PlayerWorker playerWorker = getPlayerWorker( id );
		Socket client = playerWorker.getClient();
		try {
			PrintWriter out = new PrintWriter( client.getOutputStream(), true );
			out.println( message );
		}
		catch( IOException e ) {
			System.out.println( "I/O error." );
		}
	}

	/**
	 * Sends info to all players when one of them makes a move about this move.
	 * @param id identifies the player.
	 * @param changingCards tells, whether it's card-changing turn.
	 * @param cards tells which cards are being changed.
	 * @param action the action performed.
	 * @param bid tells how big the player's bid is.
	 * @param balance tells how much money the player has left.
	 */
	public void sendMoveInfo( int id, boolean changingCards, int cards, char action, int bid, int balance ) {
		for( PlayerWorker playerWorker : playerWorkers ) {
			Socket client = playerWorker.getClient();
			try {
				PrintWriter out = new PrintWriter( client.getOutputStream(), true );
				out.println( "Game" );
				out.println( id );
				out.println( changingCards );
				out.println( cards );
				out.println( action );
				out.println( bid );
				out.println( balance );
				System.out.println( "Game" );
				System.out.println( id );
				System.out.println( changingCards );
				System.out.println( cards );
				System.out.println( action );
				System.out.println( bid );
				System.out.println( balance );
			} catch( IOException e ) {
				System.out.println( "I/O error. Move." );
			}
		}
	}

	/**
	 * Sends info at the beginning of the game.
	 * @param dealerID identifies the player.
	 */
	public void sendSetUpInfo( int dealerID ) {
		for( PlayerWorker playerWorker : playerWorkers ) {
			Socket client = playerWorker.getClient();
			try {
				PrintWriter out = new PrintWriter( client.getOutputStream(), true );
				for( Player player : table.getPlayers() ) {
					out.println( "SetUp" );
					out.println( player.getID() );
					if( player.getID() == dealerID )
						out.println( "Dealer button." );
					else if( player.getID() == ( dealerID + 1 ) % table.getPlayers().size() )
						out.println( "Small blind." );
					else if( player.getID() == ( dealerID + 2 ) % table.getPlayers().size() )
						out.println( "Big blind." );
					else
						out.println( "Custom." );
					out.println( player.getBid() );
					out.println( player.getBalance() );
				}
			}
			catch( IOException e ) {
				System.out.println( "I/O error. SetUp." );
			}
		}
	}

	/**
	 * Sends info about winnings.
	 * @param id identifies the player.
	 * @param winnings how much the player has won.
	 */
	public void sendWinInfo( int id, int winnings, int balance ) {
		for( PlayerWorker playerWorker : playerWorkers ) {
			Socket client = playerWorker.getClient();
			try {
				PrintWriter out = new PrintWriter( client.getOutputStream(), true );
				out.println( "Winners" );
				out.println( id );
				out.println( winnings );
				out.println( balance );
			} catch( IOException e ) {
				System.out.println( "I/O error. Win." );
			}
		}
	}

	@Override
	protected void finalize() throws Throwable {
		try {
			client.close();
			serverSocket.close();
		}
		catch( IOException e ) {
			System.out.println( "Closing failed." );
			System.exit( 1 );
		}
		super.finalize();
	}
}